#pragma once
#include "task_service.h"
#include "timer_service.h"

#define handle2index(handle) (handle%TQ_LIST_HASH_SIZE)

cheetah_task_service TAS;

void cheetah_thread_tinfo_init(cheetah_thread_tinfo* info) {
	info->task_ = 0;
	info->finish_count_ = 0;
	info->node_ = 0;
	info->proc_time_ = 0;
}

void cheetah_tq_list_init(cheetah_tq_list* list) {
	list->tail_ = list->head_ = 0;
}

void cheetah_cache_tq_list_init(cheetah_cache_tq_list* list) {
	list->head_ = list->tail_ = 0;
	list->lock_ = 0;
}

void cheetah_task_list_init(cheetah_task_list* list) {
	list->head_ = list->tail_ = 0;
	list->lock_ = 0;
}

void cheetah_task_tq_list_init(cheetah_task_tq_list* list) {
	list->head_ = list->tail_ = 0;
	list->lock_ = 0;
}

cheetah_thread_tinfo* cheetah_thread_tinfo_list_create(int32 thread_max) {
	cheetah_thread_tinfo* thread_info_list;
	int32 i;

	assert(thread_max > 0);
	thread_info_list = (cheetah_thread_tinfo*)malloc(sizeof(cheetah_thread_tinfo) * thread_max);
	
	for (i = 0; i < thread_max; i++) {
		cheetah_thread_tinfo_init(&thread_info_list[i]);
	}
	return thread_info_list;
}

void cheetah_task_service_tick(void* ctx, void* param);

void cheetah_task_service_init(int32 thread_max) {
	cheetah_task_service* s = &TAS;
	assert(thread_max > 0 && thread_max < THREAD_MAX);
	cheetah_tq_list_init(&s->tq_list_);
	cheetah_cache_tq_list_init(&s->cache_tq_list_);
	cheetah_task_tq_list_init(&s->task_tq_list_);
	memset(s->tq_hash_list_, 0, sizeof(s->tq_hash_list_));
	s->timer_ = cheetah_timer_create(10, cheetah_task_service_tick, 0, 0);
	s->thread_max_ = thread_max;
	s->cur_task_list_ = cheetah_thread_tinfo_list_create(s->thread_max_);
}

int32 cheetah_cache_tq_push(cheetah_cache_tq_list* list, cheetah_tq_node* node) {
	assert(node);
	ATOM_LOCK(&list->lock_);
	if (list->tail_) {
		assert(list->head_);
		list->tail_->next_ = node;
		list->tail_ = node;
	}
	else {
		list->head_ = list->tail_ = node;
	}
	ATOM_UNLOCK(&list->lock_);
	return 1;
}

cheetah_tq_node* cheetah_cache_tq_pop(cheetah_cache_tq_list* list) {
	cheetah_tq_node* node = 0;
	ATOM_LOCK(&list->lock_);
	if (list->head_) {
		node = list->head_;
		list->head_ = node->next_;
		node->next_ = 0;
		if (!list->head_) {
			list->tail_ = 0;
		}
	}
	ATOM_UNLOCK(&list->lock_);
	return node;
}

int32 cheetah_tq_push(cheetah_tq_list* list, cheetah_tq_node* node) {
	cheetah_task_service* s = &TAS;
	assert(!node->next_);
	if (node->new_) {
		uint64 idx = handle2index(node->tq_->handle_);
		cheetah_tq_hash_node* hnode = s->tq_hash_list_[idx];
		cheetah_tq_hash_node* new_hnode = 0;
		while (hnode) {
			if (hnode->tq_->handle_ == node->tq_->handle_)
			{
				return 0;
			}
			hnode = hnode->next_;
		}
		new_hnode = (cheetah_tq_hash_node*)malloc(sizeof(cheetah_tq_hash_node));
		new_hnode->tq_ = node->tq_;
		new_hnode->next_ = s->tq_hash_list_[idx];
		s->tq_hash_list_[idx] = new_hnode;
	}

	if (list->tail_) {
		assert(list->head_);
		list->tail_->next_ = node;
		list->tail_ = node;
	}
	else {
		list->head_ = list->tail_ = node;
	}
	return 1;
}

cheetah_tq* cheetah_tq_hlist_find(cheetah_handle handle) {
	cheetah_task_service* s = &TAS;
	uint64 idx = handle2index(handle);
	cheetah_tq_hash_node* hnode = s->tq_hash_list_[idx];
	while (hnode) {
		if (hnode->tq_->handle_ == handle) {
			return hnode->tq_;
		}
		hnode = hnode->next_;
	}
	return 0;
}

int32 cheetah_tq_hlist_remove(cheetah_handle handle) {
	cheetah_task_service* s = &TAS;
	uint64 idx = handle2index(handle);
	cheetah_tq_hash_node* hnode = s->tq_hash_list_[idx];
	if (hnode) {
		if (hnode->tq_->handle_ == handle) {
			s->tq_hash_list_[idx] = hnode->next_;
			free(hnode);
			return 1;
		}
		else {
			cheetah_tq_hash_node* parent = hnode;
			hnode = hnode->next_;
			while (hnode) {
				if (hnode->tq_->handle_ == handle) {
					parent->next_ = hnode->next_;
					free(hnode);
					return 1;
				}
				parent = hnode;
				hnode = hnode->next_;
			}
		}
	}
	return 0;
}

void cheetah_task_service_add_tq(cheetah_tq* tq) {
	cheetah_task_service* s;
	cheetah_tq_node* node;
	assert(tq);
	s = &TAS;
	node = (cheetah_tq_node*)malloc(sizeof(cheetah_tq_node));
	assert(node);
	node->tq_ = tq;
	node->next_ = 0;
	node->new_ = 1;
	cheetah_cache_tq_push(&s->cache_tq_list_, node);
}

int32 cheetah_service_task_tq_push(cheetah_tq_node* node) {
	cheetah_task_service* s = &TAS;
	cheetah_task_tq_list* list;
	assert(node && !node->next_);
	node->new_ = 0;
	list = &s->task_tq_list_;
	ATOM_LOCK(&list->lock_);
	if (list->tail_) {
		list->tail_->next_ = node;
		list->tail_ = node;
	}
	else { 
		list->head_ = list->tail_ = node;
	}
	ATOM_UNLOCK(&list->lock_);
	return 1;
}

cheetah_tq_node* cheetah_service_task_tq_pop() {
	cheetah_task_service* s = &TAS;
	cheetah_tq_node* node = 0;
	cheetah_task_tq_list* list = &s->task_tq_list_;
	ATOM_LOCK(&list->lock_);
	if (list->head_) {
		node = list->head_;
		list->head_ = node->next_;
		node->next_ = 0;
		if (!list->head_) {
			list->tail_ = 0;
		}
	}
	ATOM_UNLOCK(&list->lock_);
	return node;
}

void cheetah_task_service_send_task(cheetah_handle handle, cheetah_task* task) {
	cheetah_task_service* s = &TAS;
	cheetah_task_list* list = &s->cache_task_list_;
	task->handle_ = handle;
	assert(!task->next_);
	ATOM_LOCK(&list->lock_);
	if (list->tail_) {
		list->tail_->next_ = task;
		list->tail_ = task;
	}
	else {
		list->head_ = list->tail_ = task;
	}
	ATOM_UNLOCK(&list->lock_);
}

cheetah_task* cheetah_task_service_task_fetch(int32 tid) {
	cheetah_task_service* s = &TAS;
	cheetah_thread_tinfo* thread_info;
	cheetah_task* t = 0;
	cheetah_tq_node* node;
	assert(tid >= 0 && tid < s->thread_max_);
	thread_info = &s->cur_task_list_[tid];
	node = thread_info->node_ ? thread_info->node_ : cheetah_service_task_tq_pop();
	if (node) {
		t = cheetah_tq_task_pop(node->tq_);
		if (t) {
			uint64 now = cheetah_time_ms();
			thread_info->task_ = t;
			thread_info->node_ = node;
			thread_info->proc_time_ = now;
		}
	}
	return t;
}

void cheetah_task_service_task_finish(cheetah_task* t, int32 tid) {
	cheetah_task_service* s = &TAS;
	uint64 now = cheetah_time_ms();
	cheetah_thread_tinfo* thread_info;
	assert(tid >= 0 && tid < s->thread_max_);
	thread_info = &s->cur_task_list_[tid];
	if (++thread_info->finish_count_ >= thread_info->node_->tq_->frame_task_max_ 
		|| !thread_info->node_->tq_->head_) {
		cheetah_cache_tq_push(&s->cache_tq_list_, thread_info->node_);
		thread_info->finish_count_ = 0;
		thread_info->node_ = 0;
	}
	if (now - thread_info->proc_time_ > TASK_EXEC_WARNNING_TIME) {
		//todo record unsafe task


	}
	thread_info->task_ = 0;
	thread_info->proc_time_ = 0;
	if (t->param_)
	{
		
		free(t->param_);
		t->param_ = 0;
	}
	free(t);
}

void cheetah_service_check_cache_tq() {
	cheetah_task_service* s = &TAS;
	cheetah_cache_tq_list* cache_list = &s->cache_tq_list_;
	cheetah_tq_node* node = 0;
	while (node = cheetah_cache_tq_pop(cache_list)) {
		cheetah_tq_push(&s->tq_list_, node);
	}
}

void cheetah_task_service_check_cache_task() {
	cheetah_task_service* s = &TAS;
	cheetah_task_list list;
	cheetah_task* task;
	cheetah_task* add_task;
	cheetah_tq* tq;
	ATOM_LOCK(&s->cache_task_list_.lock_);
	list.head_ = s->cache_task_list_.head_;
	list.tail_ = s->cache_task_list_.tail_;
	cheetah_task_list_init(&s->cache_task_list_);
	ATOM_UNLOCK(&s->cache_task_list_.lock_);
	task = list.head_;
	while (task) {
		add_task = task;
		task = task->next_;
		add_task->next_ = 0;
		tq = cheetah_tq_hlist_find(add_task->handle_);
		if (tq) {
			cheetah_tq_task_push(tq, add_task);
		}
	}
}

void cheetah_service_task_tq_check_state() {
	cheetah_task_service* s = &TAS;
	cheetah_tq_node* parent = s->tq_list_.head_;
	cheetah_tq_node* node = 0;
	while (parent) {
		if (parent->tq_->release_) {
			s->tq_list_.head_ = parent->next_;
			parent->next_ = 0;
			cheetah_tq_hlist_remove(parent->tq_->handle_);
			REF_RELEASE(&parent->tq_->ref_);
			free(parent);
			parent = s->tq_list_.head_;
		}
		else if (parent->tq_->head_) {
			s->tq_list_.head_ = parent->next_;
			parent->next_ = 0;
			cheetah_service_task_tq_push(parent);
			parent = s->tq_list_.head_;
		}
		else {
			node = parent->next_;
			break;
		}
	}
	if (!s->tq_list_.head_) {
		s->tq_list_.tail_ = 0;
	}

	while (node) {
		if (node->tq_->release_) {
			parent->next_ = node->next_;
			node->next_ = 0;
			cheetah_tq_hlist_remove(parent->tq_->handle_);
			REF_RELEASE(&node->tq_->ref_);
			free(node);
			node = parent->next_;
		}
		else if (node->tq_->head_) {
			parent->next_ = node->next_;
			node->next_ = 0;
			cheetah_service_task_tq_push(node);
			node = parent->next_;
		}
		else {
			parent = node;
			node = node->next_;
		}
	}
}

void cheetah_task_service_tick(void* ctx, void* param) {
	cheetah_service_check_cache_tq();
	cheetah_task_service_check_cache_task();
	cheetah_service_task_tq_check_state();
}

void cheetah_task_service_start() {
	cheetah_task_service* s = &TAS;
	cheetah_timer_service_add_timer(s->timer_);
}

void cheetah_task_service_stop() {
	
}

//todo create new thread if needed...
